package com.sayice.icepusher.service;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.sayice.icepusher.cache.CacheService;
import com.sayice.icepusher.config.WeiXinConfig;
import com.sayice.icepusher.pojo.AuthInfo;
import org.jsoup.Connection;
import org.jsoup.Jsoup;

import java.io.IOException;
import java.util.function.Supplier;

/**
 * 企业微信消息推送服务
 *
 * @author shubing
 */
public class WeiXinMessageService {
    /**
     * 获取 suite_access_token 的地址
     */
    private static final String SUITE_TOKEN_URL = " https://qyapi.weixin.qq.com/cgi-bin/service/get_suite_token";
    
    /**
     * 获取 access_token 的地址
     */
    private static final String TOKEN_URL = "https://qyapi.weixin.qq.com/cgi-bin/service/get_corp_token?suite_access_token=";
    
    /**
     * 获取企业永久授权码的地址
     */
    private static final String PERMANENT_CODE_URL = "https://qyapi.weixin.qq.com/cgi-bin/service/get_permanent_code?suite_access_token=";
    
    /**
     * 发送应用消息的地址
     */
    private static final String SEND_URL = "https://qyapi.weixin.qq.com/cgi-bin/message/send?access_token=";
    
    private WeiXinMessageService() {
    }
    
    /**
     * 发送文本消息
     *
     * @param content 消息内容
     * @return 返回调用企业微信消息推送接口返回的 json 数据，若调用失败则返回 null
     */
    public static String sendText(String content) {
        return ensureAccessGet(null, WeiXinMessageService::getNewToken, () -> {
            try {
                return Jsoup.connect(SEND_URL + getToken())
                        .requestBody(String.format("{\"touser\": \"%s\", \"msgtype\": \"text\", \"agentid\": \"%s\", \"text\": {\"content\": \"%s\"}}",
                                WeiXinConfig.OPEN_USER_ID, WeiXinConfig.APP_AGENT_ID, content))
                        .method(Connection.Method.POST)
                        .ignoreContentType(true)
                        .timeout(10000)
                        .execute()
                        .body();
            } catch (Exception e) {
                e.printStackTrace();
                return null;
            }
        });
    }
    
    /**
     * 获取企业授权信息 (永久授权码、企业应用 id、open_userid)
     *
     * @param authCode 临时授权码
     * @return 授权信息 {@link AuthInfo}，若获取失败则对应属性为空串
     */
    public static AuthInfo getAuthInfo(String authCode) {
        String permanentCode = "";
        String openCorpId = "";
        String openUserId = "";
        try {
            String respBody = ensureAccessGet(null, WeiXinMessageService::getNewSuiteToken, () -> {
                try {
                    return Jsoup.connect(PERMANENT_CODE_URL + getSuiteToken())
                            .requestBody("{\"auth_code\": \"" + authCode + "\"}")
                            .method(Connection.Method.POST)
                            .ignoreContentType(true)
                            .timeout(10000)
                            .execute()
                            .body();
                } catch (Exception e) {
                    e.printStackTrace();
                    return null;
                }
            });
            botSend("getAuthInfo-response: " + respBody.replace("\"", "'"));
            JSONObject respJson = JSON.parseObject(respBody);
            permanentCode = respJson.getString("permanent_code");
            openCorpId = respJson.getJSONObject("auth_corp_info").getString("corpid");
            openUserId = respJson.getJSONObject("auth_user_info").getString("open_userid");
        } catch (Exception e) {
            e.printStackTrace();
        }
        return new AuthInfo(permanentCode, openCorpId, openUserId);
    }
    
    /**
     * 获取第三方应用凭证 suite_access_token
     *
     * @return 缓存中的 suite_access_token，若缓存中没有则重新获取
     */
    private static String getSuiteToken() {
        String suiteToken = CacheService.get("suiteToken");
        return "".equals(suiteToken) ? getNewSuiteToken() : suiteToken;
    }
    
    /**
     * 获取企业凭证 access_token
     *
     * @return 缓存中的 access_token，若缓存中没有则重新获取
     */
    private static String getToken() {
        String token = CacheService.get("token");
        return "".equals(token) ? getNewToken() : token;
    }
    
    /**
     * 获取并更新第三方应用凭证 suite_access_token
     *
     * @return 新的 suite_access_token，若获取失败则返回原来的值
     */
    private static String getNewSuiteToken() {
        String oldSuiteToken = CacheService.get("suiteToken");
        synchronized (WeiXinMessageService.class) {
            String suiteToken = CacheService.get("suiteToken");
            if (!suiteToken.equals(oldSuiteToken)) {
                return suiteToken;
            }
            String respBody = null;
            try {
                respBody = Jsoup.connect(SUITE_TOKEN_URL)
                        .requestBody(String.format("{\"suite_id\": \"%s\", \"suite_secret\": \"%s\", \"suite_ticket\": \"%s\"}",
                                WeiXinConfig.SUITE_ID, WeiXinConfig.SUITE_SECRET, CacheService.get("suiteTicket")))
                        .method(Connection.Method.POST)
                        .ignoreContentType(true)
                        .timeout(10000)
                        .execute()
                        .body();
            } catch (IOException e) {
                botSend("getNewSuiteToken-error: " + e);
                e.printStackTrace();
            }
            if (respBody != null) {
                botSend("getNewSuiteToken-response: " + respBody.replace("\"", "'"));
                suiteToken = JSON.parseObject(respBody).getString("suite_access_token");
                CacheService.set("suiteToken", suiteToken);
            }
            return suiteToken;
        }
    }
    
    /**
     * 获取并更新企业凭证 access_token
     *
     * @return 新的 access_token，若获取失败则返回空串
     */
    private static String getNewToken() {
        String oldToken = CacheService.get("token");
        synchronized (WeiXinMessageService.class) {
            String token = CacheService.get("token");
            if (!token.equals(oldToken)) {
                return token;
            }
            token = ensureAccessGet("access_token", WeiXinMessageService::getNewSuiteToken, () -> {
                try {
                    String respBody = Jsoup.connect(TOKEN_URL + getSuiteToken())
                            .requestBody(String.format("{\"auth_corpid\": \"%s\", \"permanent_code\": \"%s\"}",
                                    WeiXinConfig.OPEN_CORP_ID, WeiXinConfig.PERMANENT_CODE))
                            .method(Connection.Method.POST)
                            .ignoreContentType(true)
                            .timeout(10000)
                            .execute()
                            .body();
                    botSend("getNewToken-response: " + respBody.replace("\"", "'"));
                    return respBody;
                } catch (Exception e) {
                    botSend("getNewToken: " + e);
                    e.printStackTrace();
                    return null;
                }
            });
            if (token == null) {
                token = "";
            }
            CacheService.set("token", token);
            return token;
        }
    }
    
    /**
     * 在保证 suite_access_token/access_token 有效的前提下获取数据
     *
     * @param targetKey 待获取数据的 key，为 null 表示获取整个 json 字符串
     * @param tokenFn   获取并更新 token 的方法
     * @param fn        获取数据的方法
     * @return 返回获取到的数据 (fn的返回值)，若获取失败则返回 null
     */
    private static String ensureAccessGet(String targetKey, Supplier<String> tokenFn, Supplier<String> fn) {
        String respBody = fn.get();
        if (respBody == null) {
            return null;
        }
        JSONObject respJson = JSON.parseObject(respBody);
        String errCode = respJson.getString("errcode");
        if ("40014".equals(errCode) || "42001".equals(errCode) || "42009".equals(errCode) || "48003".equals(errCode) || "40082".equals(errCode)) {
            botSend("token 已失效");
            tokenFn.get();
            respBody = fn.get();
            if (respBody == null) {
                return null;
            }
            respJson = JSON.parseObject(respBody);
        }
        if (targetKey == null) {
            return respBody;
        }
        return respJson.getString(targetKey);
    }
    
    /**
     * 推送群聊机器人消息
     *
     * @param content 消息内容
     */
    public static void botSend(String content) {
        try {
            Jsoup.connect(WeiXinConfig.BOT_WEBHOOK)
                    .requestBody(String.format("{\"msgtype\": \"text\", \"text\": {\"content\": \"%s\"}}", content))
                    .method(Connection.Method.POST)
                    .ignoreContentType(true)
                    .timeout(5000)
                    .execute();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
